#include<iostream>
#include<queue>
#include<pthread.h>
#include<time.h>
#include<cstdlib>
#include<unistd.h>

using namespace std;

static const int N = 5;

template<class T>
class BlockQueue
{
public:
  BlockQueue(int num = N)
    :_capacity(num)
  {
    pthread_mutex_init(&_mutex,nullptr);
    pthread_cond_init(&_ConsumerCond,nullptr);
    pthread_cond_init(&_ProductorCond,nullptr);
  }

  void push(const T& key)
  {
    pthread_mutex_lock(&_mutex);//加锁
    while(IsFull())//判断临界资源是否符合条件，本质也是一种访问临界资源，所以要放在加锁的后面
    {
      pthread_cond_wait(&_ProductorCond,&_mutex);//不满足条件就阻塞
    }
    _data.push(key);//存入数据
    pthread_cond_signal(&_ConsumerCond);//有数据了，唤醒消费者
    pthread_mutex_unlock(&_mutex);//解锁
  }
  
  void pop(T& key)
  {
    pthread_mutex_lock(&_mutex);//加锁
    while(IsEmpty())
    {
      pthread_cond_wait(&_ConsumerCond,&_mutex);//不满足条件就阻塞
    }
    key = _data.front();
    _data.pop();
    pthread_cond_signal(&_ProductorCond);//有空间了，唤醒生产者
    pthread_mutex_unlock(&_mutex);//解锁
  }

  bool IsFull() {return _data.size() == _capacity;}

  bool IsEmpty() {return _data.empty();}

  ~BlockQueue()
  {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_ConsumerCond);
    pthread_cond_destroy(&_ProductorCond);
  }
private:
  queue<T> _data;
  int _capacity;
  pthread_mutex_t _mutex;
  pthread_cond_t _ConsumerCond;
  pthread_cond_t _ProductorCond;
};
